MEGpipeline_MakePLSmat.m 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2. % Reads 4D-AFNI files to generate .mat files for PLS input. %
  3. % Inputs: 4D-AFNI Input, 3D-AFNI Mask, Seed & Behavior Data. %
  4. % Last modified: Jan. 15, 2014 %
  5. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  6. % Copyright (C) 2013-2014, Michael J. Cheung
  7. %
  8. % This file is a part of the MEG & PLS Pipeline (MEGPLS). For more
  9. % details, see the documentation included with the software package.
  10. %
  11. % MEGPLS is free software: you can redistribute it and/or modify it under
  12. % the terms of the GNU General Public License version 2 as published by
  13. % the Free Software Foundation. This program is distributed in the hope
  14. % that it will be useful, but WITHOUT ANY WARRANTY; without even the
  15. % implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. % See the GNU General Public License for more details.
  17. %
  18. % You should have received a copy of the GNU General Public License along
  19. % with this program. If not, you can download the license here:
  20. % <http://www.gnu.org/licenses/old-licenses/gpl-2.0>.
  21. function PLSmat = MEGpipeline_MakePLSmat(PLSmatSettings)
  22. % Load PLSmat input & settings:
  23. PLSmat = PLSmatSettings;
  24. name = PLSmat.name;
  25. time = PLSmat.time;
  26. paths = PLSmat.paths;
  27. Opt.format = 'vector';
  28. % Clear existing Errorlog & Diary:
  29. if exist('ErrorLog_MakePLSmat.txt', 'file')
  30. system('rm ErrorLog_MakePLSmat.txt');
  31. end
  32. if exist('Diary_MakePLSmat.txt', 'file')
  33. system('rm Diary_MakePLSmat.txt');
  34. end
  35. diary Diary_MakePLSmat.txt
  36. ErrLog = fopen('ErrorLog_MakePLSmat.txt', 'a');
  37. %========================================%
  38. % CHECK FILES, DIMENSIONS & ORIENTATION: %
  39. %========================================%
  40. % Get dimensions & orientation from first .BRIK file:
  41. [~, Brik, Info, ~] = BrikLoad(paths.Afni4D{1}{1,1}, Opt);
  42. Brik = double(Brik);
  43. BrikDim4D = size(Brik);
  44. BrikDim3D = BrikDim4D(1:3);
  45. BrikOrient = Info.ORIENT_SPECIFIC;
  46. SampleInfo = Info;
  47. % Check AFNI files:
  48. MissingFiles = 0;
  49. MismatchDims = 0;
  50. for g = 1:length(name.GroupID)
  51. for s = 1:length(name.SubjID{g})
  52. for c = 1:length(name.CondID)
  53. % Check if file exists (Since no LoadFTmat):
  54. if ~exist(paths.Afni4D{g}{s,c}, 'file')
  55. fprintf(ErrLog, 'ERROR: Input AFNI file does not exist: \n %s \n\n',...
  56. paths.Afni4D{g}{s,c});
  57. MissingFiles = 1;
  58. continue;
  59. else
  60. [~, Brik, Info, ~] = BrikLoad(paths.Afni4D{g}{s,c}, Opt);
  61. end
  62. % Check dimensions:
  63. if ~isequal(size(Brik), BrikDim4D) || ~isequal(Info.ORIENT_SPECIFIC, BrikOrient)
  64. fprintf(ErrLog, ['ERROR: File has different dimensions or orientation:'...
  65. '\n File: %s \n Dims: %s \n Orient: %s \n\n'],...
  66. paths.Afni4D{g}{s,c}, num2str(size(Brik)), Info.Orientation(:,1));
  67. MismatchDims = 1;
  68. end
  69. end % Cond
  70. end % Subj
  71. end % Group
  72. if MissingFiles == 1
  73. error('ERROR: Input AFNI .BRIK file(s) missing.')
  74. end
  75. if MismatchDims == 1
  76. error('ERROR: All AFNI files must have the same Dimensions & Orientation.')
  77. end
  78. % In case of 3D AFNI files:
  79. if length(BrikDim4D) == 3
  80. BrikDim4D = [BrikDim3D, 1];
  81. BrikNumTime = 1;
  82. else
  83. BrikNumTime = BrikDim4D(4);
  84. end
  85. Brik = []; % Free memory
  86. %===================================%
  87. % ACQUIRE WHOLE-BRAIN MASK INDICES: %
  88. %===================================%
  89. if strcmp(PLSmat.MaskType, 'BrainMask')
  90. %--- If generating a data-specific mask: ---%
  91. %-------------------------------------------%
  92. if strcmp(PLSmat.DataSpecificMask, 'yes')
  93. % Only keep "inside" voxels shared by certain % of the datasets.
  94. clear LoadData BrainMask NumDatasets
  95. BrainMask = [];
  96. MaskErrors = 0;
  97. t = PLSmat.TimeIndices(1); % Inside should be same across time anyways.
  98. for g = 1:length(name.GroupID)
  99. for s = 1:length(name.SubjID{g})
  100. for c = 1:length(name.CondID)
  101. LoadData = LoadFTmat(paths.NormSource{g}{s,c,t}, 'MakePLSmat');
  102. if isempty(LoadData)
  103. MaskErrors = 1;
  104. continue;
  105. end
  106. if isempty(BrainMask)
  107. BrainMask = LoadData.inside;
  108. NumDatasets = 1;
  109. else
  110. if ~isequal(size(BrainMask), size(LoadData.inside))
  111. fprintf(ErrLog, ['ERROR: For mask creation, this dataset does'...
  112. 'not have the same dimensions as the first dataset loaded.'...
  113. '\n %s \n\n'], paths.NormSource{g}{s,c,t});
  114. MaskErrors = 1;
  115. continue;
  116. end
  117. BrainMask = BrainMask + LoadData.inside; % Add all inside data
  118. NumDatasets = NumDatasets + 1;
  119. end
  120. LoadData = []; % Free memory
  121. end % Cond
  122. end % Subj
  123. end % Group
  124. if MaskErrors == 0
  125. BrainMask = BrainMask ./ NumDatasets; % Average to get %
  126. BrainMask(find(BrainMask < PLSmat.MaskKeepVoxThresh)) = 0; % Set voxels under thresh to 0.
  127. BrainMask(find(BrainMask > 0)) = 1;
  128. BrainMask = logical(BrainMask);
  129. if ~isequal(size(BrainMask), BrikDim3D)
  130. fprintf(ErrLog, ['ERROR: Data-specific brain mask has different dimensions'...
  131. 'than the input 4D-AFNI files. \n\n']);
  132. error('ERROR: Data-specific brain mask has different dims than input 4D-AFNI files.');
  133. end
  134. BrainMaskFlat = reshape(BrainMask, 1, []); % Flatten to 1D-array (applied to flat data).
  135. BrainMaskFlatIndices = find(BrainMaskFlat > 0); % Linear voxel-indices of mask areas.
  136. BrainMaskFlat = []; % Free memory
  137. else
  138. fprintf(ErrLog, ['ERROR: Failed to generate data-specific brain mask.'...
  139. '\n This may be due to mismatched dimensions in the NormSource files.'...
  140. '\n This may also be caused by missing or broken files.']);
  141. error('ERROR: Failed to generate data-specific brain mask. See ErrorLog.');
  142. end
  143. end
  144. %--- If selecting a brain-mask file: ---%
  145. %---------------------------------------%
  146. if strcmp(PLSmat.DataSpecificMask, 'no');
  147. % Check for brain mask:
  148. if ~exist(PLSmat.BrainMaskFile, 'file')
  149. fprintf(ErrLog, 'ERROR: Input Brain-Mask does not exist: \n %s \n\n',...
  150. PLSmat.BrainMaskFile);
  151. error('ERROR: Input Brain-Mask file could not be found.')
  152. end
  153. % Move selected brain mask to AnalysisID folder:
  154. [~, OrigMaskName, ~] = fileparts(PLSmat.BrainMaskFile);
  155. CopiedMaskFile = [paths.AnalysisID,'/MASKS_TEMPLATES/',OrigMaskName,'.BRIK'];
  156. if exist(CopiedMaskFile, 'file')
  157. delete(CopiedMaskFile);
  158. delete([paths.AnalysisID,'/MASKS_TEMPLATES/',OrigMaskName,'.HEAD']);
  159. end
  160. system(['3dcopy ',PLSmat.BrainMaskFile,' ',CopiedMaskFile]);
  161. % Resample mask if needed:
  162. ResampledMaskFile = MEGpipeline_AfniResampleAnat(CopiedMaskFile, paths.Afni4D{1}{1,1});
  163. if isempty(ResampledMaskFile)
  164. fprintf(ErrLog, 'ERROR: Failed to resample brainmask file: \n %s \n\n', CopiedMaskFile);
  165. error('ERROR: Failed to resample brainmask file for PLS.')
  166. end
  167. % Get brain-only indices:
  168. [~, BrainMask, Info, ~] = BrikLoad(ResampledMaskFile, Opt);
  169. BrainMask = double(BrainMask);
  170. BrainMaskFlat = reshape(BrainMask, 1, []); % Flatten to 1D-array (applied to flat data).
  171. BrainMaskFlatIndices = find(BrainMaskFlat > 0); % Linear voxel-indices of brain-only areas.
  172. BrainMaskFlat = []; % Free memory
  173. end
  174. end % If Brain Mask
  175. % Why do we use linear-equivalent indices for masking? Easier & more consistent.
  176. % - We could use the 3D-indices from the Brain/ROI Masks instead, and apply them
  177. % to the data (for each time) prior to flattening.
  178. % - However, we need to flatten the data anyways, so this is just easier.
  179. %===========================%
  180. % ACQUIRE ROI MASK INDICES: %
  181. %===========================%
  182. if strcmp(PLSmat.MaskType, 'ROIMask')
  183. % Check for ROI mask:
  184. if ~exist(PLSmat.ROIMaskFile, 'file')
  185. fprintf(ErrLog, 'ERROR: Input ROI-Mask does not exist: \n %s \n\n',...
  186. PLSmat.ROIMaskFile);
  187. error('ERROR: Input ROI-Mask .xls could not be found.')
  188. end
  189. % Convert Mask XYZmm RAI coordinates (R-L, A-P, I-S) into AFNI IJK-indices:
  190. % Note: AFNI reads orientation in voxel storage-order (not +x, +y, +z).
  191. [ROIcoords, ROInames] = load_randy_regions(PLSmat.ROIMaskFile);
  192. for roi = 1:size(ROICoords, 1)
  193. [~, IJKafni] = AFNI_XYZcontinuous2Index(ROIcoords(roi,:), SampleInfo, 'RAI');
  194. ROIMask3DIndices(roi,:) = IJKafni + 1; % Correct for AFNI index starting at 0.
  195. end
  196. % Get ROI voxel indices:
  197. ROIMaskFlatIndices = [];
  198. ROIBlobFlatIndices = [];
  199. for roi = 1:size(ROIMask3DIndices, 1)
  200. ROIBlobFlatIndices{roi} = [];
  201. clear Roi3DIndex RoiFlatIndex
  202. % Get linear-equivalent voxel-index for ROI:
  203. Roi3DIndex = ROIMask3DIndices(roi,:);
  204. RoiFlatIndex = sub2ind(BrikDim3D, Roi3DIndex(1), Roi3DIndex(2), Roi3DIndex(3));
  205. ROIMaskFlatIndices = [ROIMaskFlatIndices; RoiFlatIndex];
  206. % Get indices for voxels around ROI (within ROI-blob):
  207. for x = PLSmat.ROIBlobSize
  208. for y = PLSmat.ROIBlobSize
  209. for z = PLSmat.ROIBlobSize
  210. clear BlobVoxel FlatIndex
  211. BlobVoxel = Roi3DIndex + [x, y, z]; % Get 3D-index of blob-voxel.
  212. for dim = 1:3 % Make sure voxel is within BRIK dims.
  213. BlobVoxel(dim) = max(BlobVoxel(dim), 1);
  214. BlobVoxel(dim) = min(BlobVoxel(dim), BrikDim3D(dim));
  215. end
  216. FlatIndex = sub2ind(BrikDim3D, BlobVoxel(1), BlobVoxel(2), BlobVoxel(3));
  217. ROIBlobFlatIndices{roi} = [ROIBlobFlatIndices{roi}; FlatIndex];
  218. end % z
  219. end % y
  220. end % x
  221. ROIBlobFlatIndices{roi} = unique(ROIBlobFlatIndices{roi});
  222. end % ROI
  223. end % If ROIMask
  224. %=================================%
  225. % GENERATE DATAMAT FOR PLS INPUT: %
  226. %=================================%
  227. % Load & flatten data, apply mask, and stack into Datamat:
  228. % Datamat{g} for each group is stacked as Subjects within Conditions.
  229. clear Datamat Bhvmat Seedmat BhvSeedmat
  230. for g = 1:length(name.GroupID)
  231. Datamat{g} = [];
  232. for c = 1:length(name.CondID)
  233. for s = 1:length(name.SubjID{g})
  234. clear Brik BrikFlatXYZ BrikMasked BrikFullFlat
  235. [~, Brik, ~, ~] = BrikLoad(paths.Afni4D{g}{s,c}, Opt);
  236. Brik = double(Brik); % Data is (x, y, z, time)
  237. % Flatten data for each time and apply mask:
  238. % Note: Cannot apply linear-equivalent of 3D-indices to 4D-data.
  239. % Therefore, need to apply mask separately for each time-index.
  240. if strcmp(PLSmat.MaskType, 'BrainMask')
  241. BrikFlatXYZ = reshape(Brik, [], BrikDim4D(4)); % Data is (X*Y*Z, time)
  242. BrikMasked = BrikFlatXYZ(BrainMaskFlatIndices,:); % Mask data for each time
  243. elseif strcmp(PLSmat.MaskType, 'ROIMask')
  244. for t = 1:BrikNumTime
  245. BrikFlatXYZ = reshape(squeeze(Brik(:,:,:,t)), 1, []) % Data is (X*Y*Z)
  246. % Get mean-signal of ROI-blob for each ROI:
  247. for roi = 1:length(ROIBlobFlatIndices)
  248. MeanBlobData = mean(BrikFlatXYZ(ROIBlobFlatIndices{roi}));
  249. BrikMasked(roi,t) = MeanBlobData;
  250. end
  251. end
  252. end
  253. BrikMasked = BrikMasked(:, [PLSmat.TimeIndices]); % Select time-indices.
  254. BrikFullFlat = reshape(BrikMasked, 1, []); % Data is 1D-array (X*Y*Z*time)
  255. Datamat{g} = [Datamat{g}; BrikFullFlat]; % Add flat BRIK to group datamat.
  256. end % Subj
  257. end % Cond
  258. % Check datamat dimensions:
  259. CorrectNumRows = (length(name.SubjID{g}) * length(name.CondID));
  260. if size(Datamat{g}, 1) ~= CorrectNumRows
  261. fprintf(ErrLog, ['ERROR: Datamat group has incorrect # of rows:'...
  262. '\n Group: %s \n Correct # rows: %s \n Current # rows: %s \n\n'],...
  263. name.GroupID{g}, num2str(CorrectNumRows), num2str(size(Datamat{g}, 1)));
  264. error('ERROR: Error creating Datamat. See ErrorLog.')
  265. end
  266. end % Group
  267. Brik = []; % Free memory
  268. BrikMasked = [];
  269. BrikFlatXYZ = [];
  270. BrikFullFlat = [];
  271. %======================================%
  272. % GENERATE SEEDMAT FOR BEHAVIORAL PLS: %
  273. %======================================%
  274. % Seedmat & Bhvmat arrangement:
  275. % - Each column represents a seed or behavioral measurement.
  276. % - Rows represent datasets (Stacked subjects within conditions within groups).
  277. % - Subjs, Conds, and Grps should be loaded in the same order as Datamat.
  278. % - Note: Groups are stacked prior to PLS input (unlike Datamats{g}).
  279. if ~isempty(PLSmat.SeedFile)
  280. if ~exist(PLSmat.SeedFile, 'file')
  281. fprintf(ErrLog, 'ERROR: Seed .xls does not exist: \n %s \n\n', PLSmat.SeedFile);
  282. error('ERROR: Input Seed .xls file could not be found.')
  283. end
  284. % Load seed .xls data:
  285. xlsdata = [];
  286. xlsdata = importdata(PLSmat.SeedFile);
  287. if isunix || ismac % Seed data starts at line 24.
  288. SeedCoords(:,1) = xlsdata.data([24:end],1); % x
  289. SeedCoords(:,2) = xlsdata.data([24:end],2); % y
  290. SeedCoords(:,3) = xlsdata.data([24:end],3); % z
  291. elseif ispc
  292. SeedCoords = xlsdata.data(:,[1:3]);
  293. end
  294. NumSeeds = size(SeedCoords, 1);
  295. SeedOrient = xlsdata.textdata{21,2}; % orientation (voxel-order)
  296. SeedRegions = xlsdata.textdata([24:end],[5:6]);
  297. if size(xlsdata.data, 2) == 3 % In case all times were specified as ranges
  298. xlsdata.data(:,4) = NaN;
  299. end
  300. % Get time-indices for each seed-coordinate:
  301. TempDataCoords(:,1) = xlsdata.data([24:end],4); % time
  302. TempTextCoords(:,1) = xlsdata.textdata([24:end],4); % in case where time-range specified
  303. for seed = 1:NumSeeds
  304. if ~isnan(TempDataCoords(seed,1))
  305. SeedTimes{seed} = TempDataCoords(seed,1);
  306. else
  307. SeedTimes{seed} = str2num(TempTextCoords{seed});
  308. if isempty(SeedTimes{seed})
  309. fprintf(ErrLog, 'ERROR: Time-indices for seeds were not entered properly. \n\n');
  310. error('ERROR: Time-indices for seeds were not entered properly.')
  311. end
  312. end
  313. end
  314. SeedTimes = SeedTimes';
  315. clear TempDataCoords TempTextCoords
  316. % Check seed .xls data:
  317. if ~strcmp(xlsdata.textdata{21,1}, 'Orientation:')
  318. fprintf(ErrLog, 'ERROR: File is not a proper seed .xls file. \n\n');
  319. error('ERROR: File loaded is not a proper seed .xls file.')
  320. end
  321. if ~isempty(find(isnan(SeedCoords)))
  322. fprintf(ErrLog, 'ERROR: SeedCoords contain NaNs (Missing data cells?). \n\n');
  323. error('ERROR: SeedCoord data contains NaNs. Check .xls for missing cells.')
  324. end
  325. if ~isempty(find(cellfun(@isempty, SeedRegions)))
  326. fprintf(ErrLog, 'ERROR: Some seeds are missing region names. \n\n');
  327. error('ERROR: Seeds are missing region names. Check .xls file.')
  328. end
  329. if isempty(SeedOrient)
  330. fprintf(ErrLog, 'ERROR: SeedCoord orient not specified in .xls file. \n\n');
  331. error('ERROR: SeedCoord orientation not specified in .xls file.')
  332. end
  333. AllTimeIndices = 1:size(time.Windows, 1);
  334. for seed = 1:NumSeeds
  335. if min(ismember(SeedTimes{seed}, AllTimeIndices)) == 0
  336. fprintf(ErrLog, 'ERROR: Seed time-indices exceed data time-indices. \n\n');
  337. error('ERROR: Seed time-indices exceed data time-indices.')
  338. end
  339. end
  340. % Generate name for each seed:
  341. for seed = 1:NumSeeds
  342. SeedTimeIndex = SeedTimes{seed};
  343. if length(SeedTimeIndex) == 1
  344. SeedNames{seed} = [SeedRegions{seed,1},'_',...
  345. SeedRegions{seed,2},'_',time.str.Windows{SeedTimeIndex,3}];
  346. else
  347. SeedNames{seed} = [SeedRegions{seed,1},'_',...
  348. SeedRegions{seed,2},'_Avg',num2str(length(SeedTimeIndex)),'TimeIndices'];
  349. end
  350. SeedNames{seed} = deblank(SeedNames{seed});
  351. SeedNames{seed} = regexprep(SeedNames{seed}, '\s+', '_');
  352. end
  353. % Convert seed coords from XYZmm to AFNI 4D IJK-index:
  354. % Note: AFNI reads orientation in voxel storage-order (not +x, +y, +z).
  355. if strcmp(SeedOrient, 'IJK') || strcmp(SeedOrient, 'IJKafni')
  356. Seeds3DIndices = SeedCoords;
  357. else
  358. for seed = 1:NumSeeds
  359. clear SeedXYZ IJKafni
  360. SeedXYZ = SeedCoords(seed,:); % XYZmm of current seed
  361. [~, IJKafni] = AFNI_XYZcontinuous2Index(SeedXYZ, SampleInfo, SeedOrient);
  362. Seeds3DIndices(seed,:) = [IJKafni+1]; % (I, J, K of current seed)
  363. end
  364. end
  365. % Get voxel indices for each seed:
  366. SeedFlatIndices = [];
  367. SeedBlobFlatIndices = [];
  368. for seed = 1:NumSeeds
  369. SeedFlatIndices{seed} = [];
  370. SeedBlobFlatIndices{seed} = [];
  371. clear Seed3DIndex
  372. % Get linear-equivalent voxel-index for seed:
  373. Seed3DIndex = Seeds3DIndices(seed,:);
  374. for t = SeedTimes{seed} % see below for description
  375. clear SeedFlatIndex
  376. SeedFlatIndex = sub2ind...
  377. (BrikDim4D, Seed3DIndex(1), Seed3DIndex(2), Seed3DIndex(3), t);
  378. SeedFlatIndices{seed} = [SeedFlatIndices{seed}; SeedFlatIndex];
  379. % Get linear-equivalent indices for voxels around seed (within seed-blob):
  380. % Later on, data from all voxels within the "seed blob" will be averaged.
  381. for x = PLSmat.SeedBlobSize
  382. for y = PLSmat.SeedBlobSize
  383. for z = PLSmat.SeedBlobSize
  384. clear BlobVoxel FlatIndex
  385. BlobVoxel = [Seed3DIndex,t] + [x, y, z, 0]; % Get 4D-index of blob-voxel.
  386. for dim = 1:4 % Make sure voxel is within BRIK dims.
  387. BlobVoxel(dim) = max(BlobVoxel(dim), 1);
  388. BlobVoxel(dim) = min(BlobVoxel(dim), BrikDim4D(dim));
  389. end
  390. FlatIndex = sub2ind(BrikDim4D,...
  391. BlobVoxel(1), BlobVoxel(2), BlobVoxel(3), BlobVoxel(4));
  392. SeedBlobFlatIndices{seed} = [SeedBlobFlatIndices{seed}; FlatIndex];
  393. end % z
  394. end % y
  395. end % x
  396. end % t
  397. SeedBlobFlatIndices{seed} = unique(SeedBlobFlatIndices{seed});
  398. % Later on, for each seed, all indices within the "seed blob" will be averaged.
  399. % - In the case of users specifying a time-range or multiple time-indices for
  400. % a given seen, we add voxel-data at all specified time-indices to the seed blob.
  401. % - Since all data from all voxel indices within a "seed blob" are averaged later
  402. % on, this will mean data from each time-index is also averaged as desired.
  403. end % Seed
  404. % Extract data for each seed and load into {Group}(Subj,Cond,Bhv):
  405. GroupSeedData = [];
  406. for g = 1:length(name.GroupID)
  407. for s = 1:length(name.SubjID{g})
  408. for c = 1:length(name.CondID)
  409. [~, Brik, Info, ~] = BrikLoad(paths.Afni4D{g}{s,c}, Opt);
  410. Brik = double(Brik);
  411. % Note: Do not need to flatten data prior to applying seed-indices.
  412. % Applying linear-equivalent of 4D-indices to 4D-data.
  413. for seed = 1:NumSeeds
  414. GroupSeedData{g}(s,c,seed) = mean(Brik(SeedBlobFlatIndices{seed}));
  415. end
  416. end % Cond
  417. end % Subj
  418. end % Group
  419. % Reshape to stack Subjects within Conditions: {Group}(Subj*Cond,Seed)
  420. % Then stack Groups to get Seedmat (Subj*Cond*Group, Seed).
  421. Seedmat = [];
  422. for g = 1:length(name.GroupID)
  423. GroupSeedData{g} = reshape(GroupSeedData{g}, [], NumSeeds);
  424. Seedmat = [Seedmat; GroupSeedData{g}];
  425. end
  426. % Check dims on Seedmat:
  427. CorrectNumRows = sum(cellfun(@numel, name.SubjID)) * length(name.CondID);
  428. if size(Seedmat, 1) ~= CorrectNumRows
  429. fprintf(ErrLog, ['ERROR: Seedmat has incorrect # of rows:'...
  430. '\n Correct # Rows: %s \n Current # rows: %s \n\n'],...
  431. num2str(CorrectNumRows), num2str(size(Seedmat, 1)));
  432. error('ERROR: Error creating Seedmat. See ErrorLog.')
  433. end
  434. Brik = []; % Free memory
  435. end % If SeedFile
  436. %===========================================%
  437. % GENERATE BEHAVIOR MAT FOR BEHAVIORAL PLS: %
  438. %===========================================%
  439. % Seedmat & Bhvmat arrangement:
  440. % - Each column represents a seed or behavioral measurement.
  441. % - Rows represent datasets (Stacked subjects within conditions within groups).
  442. % - Subjs, Conds, and Grps should be loaded in the same order as Datamat.
  443. % - Note: Groups are stacked prior to PLS input (unlike Datamats{g}).
  444. if ~isempty(PLSmat.BhvFile)
  445. if ~exist(PLSmat.BhvFile, 'file')
  446. fprintf(ErrLog, 'ERROR: Behavior .xls does not exist: \n %s \n\n', PLSmat.BhvFile);
  447. error('ERROR: Input behavior .xls file could not be found.')
  448. end
  449. % Load behavior file:
  450. xlsdata = [];
  451. xlsdata = importdata(PLSmat.BhvFile);
  452. BhvSheets = fieldnames(xlsdata.textdata);
  453. BhvSheets(strcmp(BhvSheets, 'Instructions')) = [];
  454. NumBhvs = length(BhvSheets);
  455. TotalNumRows = sum(cellfun(@numel, name.SubjID)); % #Rows = Total #Subj
  456. TotalNumCols = length(name.CondID);
  457. % Check if .xls file is proper format:
  458. if ~strcmp(xlsdata.textdata.(BhvSheets{1}){1,1}, 'Behavior:')
  459. fprintf(ErrLog, 'ERROR: File is not a proper behavior .xls file. \n\n');
  460. error('ERROR: File loaded is not a proper behavior .xls file.')
  461. end
  462. % Get behavior sheets & check for errors:
  463. for b = 1:NumBhvs
  464. BhvData{b} = xlsdata.data.(BhvSheets{b});
  465. BhvNames{b} = xlsdata.textdata.(BhvSheets{b}){1,2};
  466. BhvNames{b} = deblank(BhvNames{b});
  467. BhvNames{b} = regexprep(BhvNames{b}, '\s+', '_');
  468. if size(BhvData{b}, 1) ~= TotalNumRows % Rows = Group+Subj in bhv .xls
  469. fprintf(ErrLog, ['ERROR: Behavior .xls file has incorrect # of rows:'...
  470. '\n Correct # Rows = Total # Subj: %s \n Current # Rows: %s \n\n'],...
  471. num2str(TotalNumRows), num2str(size(BhvData{b}, 1)));
  472. error('ERROR: Behavior .xls file has incorrect # rows. See ErrorLog.')
  473. end
  474. if size(BhvData{b}, 2) ~= TotalNumCols % Columns = Conds in bhv .xls
  475. fprintf(ErrLog, ['ERROR: Behavior .xls file has incorrect # of columns:'...
  476. '\n Correct # Cols = # Conds: %s \n Current # Cols: %s \n\n'],...
  477. num2str(TotalNumCols), num2str(size(BhvData{b}, 2)));
  478. error('ERROR: Behavior .xls file has incorrect # columns. See ErrorLog.')
  479. end
  480. if ~isempty(find(isnan(BhvData{b})))
  481. fprintf(ErrLog, 'ERROR: Behavior data contains NaNs (Missing cells?) \n\n');
  482. error('ERROR: Behavior data contains NaNs. Check .xls for missing cells.');
  483. end
  484. if isempty(BhvNames{b})
  485. fprintf(ErrLog, 'ERROR: Behavior name is missing. \n\n');
  486. error('ERROR: No name specified for behavior.');
  487. end
  488. end % Bhv
  489. % Load behavior data into {Group}(Subj,Cond,Bhv) for easy reshaping:
  490. % For each group, get data from corresponding subject rows:
  491. GroupBhvData = [];
  492. for b = 1:NumBhvs
  493. StartRow = 1; % Reset for new bhv sheet
  494. for g = 1:length(name.GroupID)
  495. EndRow = StartRow + length(name.SubjID{g}) - 1;
  496. GroupBhvData{g}(:,:,b) = BhvData{b}([StartRow:EndRow],[1:end]);
  497. StartRow = StartRow + length(name.SubjID{g});
  498. end
  499. end
  500. % Reshape to stack Subjects within Conditions: {Group}(Subj*Cond,Bhv)
  501. % Then stack Groups to get BehaviorMat (Subj*Cond*Group, Bhv).
  502. Bhvmat = [];
  503. for g = 1:length(name.GroupID)
  504. GroupBhvData{g} = reshape(GroupBhvData{g}, [], NumBhvs);
  505. Bhvmat = [Bhvmat; GroupBhvData{g}];
  506. end
  507. % Check dims on Bhvmat:
  508. CorrectNumRows = sum(cellfun(@numel, name.SubjID)) * length(name.CondID);
  509. if size(Bhvmat, 1) ~= CorrectNumRows
  510. fprintf(ErrLog, ['ERROR: Bhvmat has incorrect # of rows:'...
  511. '\n Correct # Rows: %s \n Current # rows: %s \n\n'],...
  512. num2str(CorrectNumRows), num2str(size(Bhvmat, 1)));
  513. error('ERROR: Error creating Bhvmat. See ErrorLog.')
  514. end
  515. end % If BhvFile
  516. %=========================================%
  517. % IF BOTH LOADED, MERGE BHVMAT & SEEDMAT: %
  518. % Note: Seeds are loaded after Behaviors. %
  519. %=========================================%
  520. if ~isempty(PLSmat.SeedFile) && ~isempty(PLSmat.BhvFile)
  521. BhvSeedmat = [Bhvmat, Seedmat];
  522. BhvSeedNames = [BhvNames, SeedNames];
  523. if size(BhvSeedmat, 2) ~= (NumBhvs + NumSeeds)
  524. fprintf(ErrLog, ['ERROR: BhvSeedmat has incorrect # of columns.:'...
  525. '\n Correct # Cols = NumBhvs + NumSeeds = %s \n Current # Cols: %s \n\n'],...
  526. num2str(NumBhvs + NumSeeds), num2str(size(BhvSeedmat, 2)));
  527. error('ERROR: Error merging behaviors and seeds. See ErrorLog.')
  528. end
  529. end
  530. %============================%
  531. % SAVE VARIABLES TO PLS MAT: %
  532. %============================%
  533. % Get datamat & brik info:
  534. PLSmat.Datamat = Datamat;
  535. PLSmat.BrikDim3D = BrikDim3D;
  536. PLSmat.BrikDim4D = BrikDim4D;
  537. PLSmat.BrikNumTime = BrikDim4D(4);
  538. PLSmat.BrikOrient = Info.ORIENT_SPECIFIC;
  539. PLSmat.BrikSampleInfo = SampleInfo;
  540. % Get mask info:
  541. switch PLSmat.MaskType
  542. case 'BrainMask'
  543. PLSmat.BrainMask = BrainMask;
  544. PLSmat.BrainMaskFlatIndices = BrainMaskFlatIndices;
  545. case 'ROIMask'
  546. PLSmat.ROIMaskCoords.RAImm = ROIcoords;
  547. PLSmat.ROIMaskCoords.Names = ROInames;
  548. PLSmat.ROIMask3DIndices = ROIMask3DIndices;
  549. PLSmat.ROIMaskFlatIndices = ROIMaskFlatIndices;
  550. PLSmat.ROIBlobsFlatIndices = ROIBlobsFlatIndices;
  551. end
  552. % Get bhvmat info:
  553. if exist('BhvSeedmat', 'var') && ~isempty(PLSmat.BhvFile) && ~isempty(PLSmat.SeedFile)
  554. PLSmat.BhvType = 'BhvSeed';
  555. PLSmat.Bhvmat = BhvSeedmat;
  556. PLSmat.BhvNames = BhvSeedNames;
  557. PLSmat.SeedCoords = SeedCoords;
  558. PLSmat.SeedOrient = SeedOrient;
  559. elseif exist('Bhvmat', 'var') && ~isempty(PLSmat.BhvFile)
  560. PLSmat.BhvType = 'Bhv';
  561. PLSmat.Bhvmat = Bhvmat;
  562. PLSmat.BhvNames = BhvNames;
  563. elseif exist('Seedmat', 'var') && ~isempty(PLSmat.SeedFile)
  564. PLSmat.BhvType = 'Seed';
  565. PLSmat.Bhvmat = Seedmat;
  566. PLSmat.BhvNames = SeedNames;
  567. PLSmat.SeedCoords = SeedCoords;
  568. PLSmat.SeedTimes = SeedTimes;
  569. PLSmat.SeedOrient = SeedOrient;
  570. %** save SeedsFlatIndices and SeedBlobsFlatIndices?
  571. end
  572. %=================%
  573. if exist([pwd,'/ErrorLog_MakePLSmat.txt'], 'file')
  574. LogCheck = dir('ErrorLog_MakePLSmat.txt');
  575. if LogCheck.bytes ~= 0 % File is not empty
  576. open('ErrorLog_MakePLSmat.txt');
  577. else
  578. delete('ErrorLog_MakePLSmat.txt');
  579. end
  580. end
  581. fclose(ErrLog);
  582. diary off